package com.minelx.statementsplus.cha.base;

import com.minelx.httpclient.core.Requests;
import com.minelx.httpclient.core.request.IResponse;
import com.minelx.sequence.common.Templates;
import com.minelx.util.Files;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.minelx.statementsplus.cha.base.HttpChaSession.swapBVAL;
import static java.nio.charset.StandardCharsets.UTF_8;

public class ChaRequestMapper {

	public static final String NEXT_LINE = "X13X10";

	public static final int RETRYING_COUNT = 10;

	public IResponse requestDoWork(String sessionId,
								   String secretKey,
								   TaskCommitment commitment,
								   long downloadNumber,
								   long bval,
								   int outputType) {
		Map<String, String> parameters = new HashMap<>();
		parameters.put("secretKey", secretKey);
		parameters.put("outputType", String.valueOf(outputType));
		parameters.put("sessionId", sessionId);
		parameters.put("downloadNumber", String.valueOf(downloadNumber));
		parameters.put("loopId", commitment.loopId());
		String majorSql = swapBVAL(commitment.majorSql(), bval);
		parameters.put("majorSql", encode(majorSql, UTF_8));
		String childSql = commitment.childSql().replace("\n", NEXT_LINE);
		parameters.put("childSql", encode(childSql, UTF_8));
		parameters.put("additionalValues", commitment.additionalValues());
		parameters.put("multipleValues", commitment.multipleValues());
		return fetching(renderAll(Files.toString("/requests/execute.request"), parameters));
	}

	public IResponse requestExplain(String sessionId, String secretKey, String majorSql) {
		// load parameters
		Map<String, String> parameters = new HashMap<>();
		parameters.put("majorSql", encode(majorSql, UTF_8));
		parameters.put("secretKey", secretKey);
		parameters.put("sessionId", sessionId);
		// parse to request
		return fetching(renderAll(Files.toString("/requests/explain.request"), parameters));
	}

	public IResponse requestLogin(String sessionId, String user, String password) {
		Map<String, String> parameters = new HashMap<>();
		parameters.put("user", user);
		parameters.put("password", password);
		parameters.put("sessionId", sessionId);
		return fetching(renderAll(Files.toString("/requests/login.request"), parameters));
	}

	private IResponse fetching(String requestAsText) {
		return fetching0(requestAsText, RETRYING_COUNT);
	}

	private IResponse fetching0(String requestAsText, int retryingCount) {
		try {
			return Requests.parse(requestAsText).fetch();
		} catch (Exception exceptionWhileFetching) {
			if (retryingCount == 0) { // 0
				throw new RuntimeException("error while fetching response: " + requestAsText, exceptionWhileFetching);
			}
			// retrying ...
			retryingCount--;
			// have 2 seconds sleep before retrying
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
				throw new RuntimeException("something is wrong here", e);
			}
			return fetching0(requestAsText, retryingCount);
		}
	}

	public static String renderAll(String source, Map<String, String> parameters) {
		return new Templates(source).render(parameters);
	}

	public static String loginForSessionId(String user, String password) {
		String sessionId = generateSessionId();
		new ChaRequestMapper().requestLogin(sessionId, user, password);
		return sessionId;
	}

	private static String generateSessionId() {
		return UUID.randomUUID().toString()
				.replace("-", "")
				.substring(0, 26);
	}

	private static String encode(String majorSql, Charset charset) {
		try {
			return URLEncoder.encode(majorSql, charset.name());
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("error while doing url encoding to: " + majorSql, e);
		}
	}
}
